
#include <iostream>
#include <math.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <csignal>

// Loggers
#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/propertyconfigurator.h>

// Thrift
#include <transport/TSocket.h>
#include <transport/TServerSocket.h>

// BOOST
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>

#include "anyoption.h"
#include "ringer_constants.h"
#include "RingerMetaServer.h"
#include "Ringer.h"
#include "RingerFS.h"
#include "FileUtil.h"

namespace fs = boost::filesystem;

static const int RINGER_MAX_IP_SIZE = 256;
static const int RINGER_GOOD_HOSTIP_COUNT = 1;
static std::string RINGER_DEFAULT_NAME = "127.0.0.1";

mode_t g_dir_create_mode = 0;
std::string g_ringer_l_cache;
ringer::rpath_t g_ringer_file_util_client_path_prefix;

#ifndef __WIN32__
#include <ctype.h>
void strupr(char* sz)
{
	int len = strlen(sz);
	for (int i = 0; i < len; i++)
		sz[i] = (char) toupper(sz[i]);
}
#endif

void SetRingeDirCreateMode(mode_t mode){
  g_dir_create_mode = mode;
}

void SetRingerLCache(std::string& lc){
  g_ringer_l_cache = lc;
}

void SetRingerPathPrefix(ringer::rpath_t pre){
  g_ringer_file_util_client_path_prefix = pre;
}

/**
 Turn a ringer path into something the underlaying FS can understand
 */
void LocalizeRingerPath(const char *path, string& local_path){
  local_path = path;
  if (local_path.find(g_ringer_l_cache) == string::npos){
    local_path = g_ringer_l_cache + local_path;
  }
}

/**
 Builds up a complete list of all files in a given directory.
 -- Note -- expects that the path is a ringer path, NOT a localized path
 */
void ListAllSubFiles(const char *path, vector<string>& files){
  
  vector<string> dirs;
  vector<string> my_files;
  
  string local_path;
  LocalizeRingerPath(path, local_path);
  
  GetDirList(local_path.c_str(), my_files, dirs);
  for (int i=0; i<my_files.size(); i++){
    string next_file = path;
    if (next_file.at(next_file.length()-1) != FS_SEPERATOR)
      next_file += FS_SEPERATOR;
    next_file += my_files.at(i);
    files.push_back(next_file);
  }
  
  for (int i=0; i<dirs.size(); i++){
    string next_dir = path;
    if (next_dir.at(next_dir.length()-1) != '/')
      next_dir += '/';
    next_dir += dirs.at(i);
    files.push_back(next_dir);
    ListAllSubFiles(next_dir.c_str(), files);
  }
}

/**
 Turn a Fuse path into a Ringer path
 */

void RingerizeFusePath(const char *path, string &ringer_path){
  
  ringer_path = path;
  
  if (ringer_path.find(g_ringer_file_util_client_path_prefix) == string::npos){
    ringer_path = g_ringer_file_util_client_path_prefix + ringer_path;
  }
}

/** 
 Reads a block of data from disk.
 
 @return Number of blocks actually read.
 */

int
ReadLocalBlock(ringer::RingerBlock& _return, const ringer::rpath_t& local_path, const int32_t offset, const int32_t length){
  
  char * buffer; // intermediate stage to keep data
  ifstream is; // in stream
  int posit;
  
  // if so, return the requested parts of the file
  is.open (local_path.c_str(), ios::binary );
  
  // allocate memory:
  buffer = new char [length];
  
  // seek to the beginning of the file we are intersted in
  is.seekg (offset);
  
  // read data as a block:
  is.read (buffer,length);
  posit = is.tellg();
  is.close();
  
  _return.block_size = ringer::g_ringer_constants.RINGER_BLOCK_SIZE;
  _return.block_value = buffer;
  
  delete[] buffer;
  return posit;
}


/**
 Write the Given bytes to disk.
 
 @return Number of bytes actually written.
 */

int WriteReturnValueToDisk(const string& local_path, const ringer::RingerBlock& r_block, const int offset){
  
  // first, create any directories which might need creating
  fs::path full_path( fs::initial_path<fs::path>() );
  fs::path base_path; // build this up from the root
  full_path = fs::system_complete( fs::path( local_path, fs::native ) );	
  for( fs::path::iterator i(full_path.begin()); i != full_path.end(); ++i ){
    base_path /= (*i);
    if (!fs::exists(base_path) && base_path != full_path){ // if it doesn't exist, build it!
      mkdir(base_path.string().c_str(), g_dir_create_mode);
    }
  }
  
  // open the file  
  int posit;
  ofstream outfile (local_path.c_str(), ofstream::binary);
  // seek to the offset
  outfile.seekp(offset);
  
  // write
  outfile.write (r_block.block_value.c_str(), r_block.block_size);
  posit = outfile.tellp();
  outfile.close();
  
  return (posit - offset);
}

// First, it it in the local cache. 
// Secoundly, has a new version of the file been written somewhere?
bool IsFileInLocalCacheAndStillCurrent(const char* path, ringer::RNode &rn){
  if (!IsFileInLocalCache(path))
    return false;
  
  struct stat stbuf;
  string local_path;
  LocalizeRingerPath(path, local_path);
  
  fs::path full_path(local_path);
  stat(full_path.native_file_string().c_str(), &stbuf);
  
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
  if (stbuf.st_mtimespec.tv_sec < rn.mtime.time_sec) 
    return false;
  
  if (stbuf.st_mtimespec.tv_sec < rn.mtime.time_sec && stbuf.st_mtimespec.tv_nsec < rn.mtime.time_nsec ) 
    return false; 
#else
  if (stbuf.st_mtime < rn.mtime.time_sec) 
    return false;
#endif
  
  return true;
}


// keep list of the files in the local cache in memory. Return true if a file is in the local cache
bool IsFileInLocalCache(const char* path){
	struct stat sb;
	return (stat(path, &sb) == 0)? true: false;
}

// From a path, create a RNode
/**
 Assume that path is the ringer path
 */
void RnodeFromPath(const char *path, ringer::RNode &rn){
  struct stat stbuf;
  string local_path;
  LocalizeRingerPath(path, local_path);
  
  if (IsFileInLocalCache(local_path.c_str())){
    fs::path full_path(local_path);
    stat(full_path.native_file_string().c_str(), &stbuf);
    rn.path = path;
    rn.fuse_path = path; // this is what fuse sees.
    
    string parent;
    ExtractParentDirname(path, parent);
    rn.parent_path = parent;
    rn.mode = stbuf.st_mode;
    rn.nlink = stbuf.st_nlink;
    rn.uid = stbuf.st_uid;
    rn.gid = stbuf.st_gid;
        
    rn.atime = ringer::TimeStr();
    rn.mtime = ringer::TimeStr();
    rn.ctime = ringer::TimeStr();
    
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
    rn.atime.time_sec = stbuf.st_atimespec.tv_sec;
    rn.atime.time_nsec = stbuf.st_atimespec.tv_nsec;
    
    rn.mtime.time_sec = stbuf.st_mtimespec.tv_sec;
    rn.mtime.time_nsec = stbuf.st_mtimespec.tv_nsec;
    
    rn.ctime.time_sec = stbuf.st_ctimespec.tv_sec; 
    rn.ctime.time_nsec = stbuf.st_ctimespec.tv_nsec;
    
    rn.flags = stbuf.st_flags;
    rn.gen = stbuf.st_gen;
#else
    rn.atime.time_sec = stbuf.st_atime;
    rn.atime.time_nsec = 0;
    
    rn.mtime.time_sec = stbuf.st_mtime;
    rn.mtime.time_nsec = 0;
    
    rn.ctime.time_sec = stbuf.st_ctime; 
    rn.ctime.time_nsec = 0;
#endif
    
    rn.offset = stbuf.st_size; 
    rn.number_blocks = stbuf.st_blocks;
    rn.blksize = stbuf.st_blksize;
    
    // on creation, signify no user
    rn.writer = ringer::NO_USER;
    
    // things that don't really translate
    // @TODO -- how should these be handled?
    rn.st_dev = stbuf.st_dev;
    rn.st_ino = stbuf.st_ino;
    rn.st_rdev = stbuf.st_rdev;
    
    rn.mds_owner.host_name = ringer::RINGER_DEFUALT_MDS_PARENT_HOST;
    
    if (fs::is_directory( full_path )){
      rn.node_type = ringer::RDIRECTORY;
      // Since this is a dir, add in all of the files this dir contains
      fs::directory_iterator end_iter;
      for ( fs::directory_iterator dir_itr( full_path );
           dir_itr != end_iter;
           ++dir_itr ) {
        if( fs::is_directory(dir_itr->status()) ||  fs::is_regular(dir_itr->status()) ){
          rn.files.insert(dir_itr->path().leaf());
        }
      }
      // Lastly, if this is the root dir, add a dir to get to other owner's files
      if (string(path) == ringer::RINGER_ROOT){
        rn.files.insert(ringer::RINGER_OTHER_HOSTS_DIR);
      } 
    } else if (fs::is_regular( full_path )) {
      rn.node_type = ringer::RFILE;
    } else { 
      rn.node_type = ringer::RNODE;
    }
  }
}

// From an RNode, full up a stat buffer
// Assume here that stbuf is a valid pointer
void StatFromRNode(ringer::RNode &rn, struct stat *stbuf){
    
  stbuf->st_mode = rn.mode;
  stbuf->st_nlink = rn.nlink;
  stbuf->st_uid = rn.uid;
  stbuf->st_gid = rn.gid;
  
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
  stbuf->st_atimespec.tv_sec = rn.atime.time_sec;
  stbuf->st_atimespec.tv_nsec = rn.atime.time_nsec;
  
  stbuf->st_mtimespec.tv_sec =  rn.mtime.time_sec;
  stbuf->st_mtimespec.tv_nsec = rn.mtime.time_nsec;
  
  stbuf->st_ctimespec.tv_sec = rn.ctime.time_sec;
  stbuf->st_ctimespec.tv_nsec = rn.ctime.time_nsec;
  
  stbuf->st_flags = rn.flags;
  stbuf->st_gen = rn.gen;
#else
  stbuf->st_atime = rn.atime.time_sec;
  //stbuf->st_atimensec = rn.atime.time_nsec;
  
  stbuf->st_mtime =  rn.mtime.time_sec;
  //stbuf->st_mtimensec = rn.mtime.time_nsec;
  
  stbuf->st_ctime = rn.ctime.time_sec;
  //stbuf->st_ctimensec = rn.ctime.time_nsec;
#endif
  
  stbuf->st_size = rn.offset; 
  stbuf->st_blocks = rn.number_blocks;
  stbuf->st_blksize = rn.blksize;
  
  // things that don't really translate
  // @TODO -- how should these be handled?
  stbuf->st_dev = rn.st_dev;
  stbuf->st_ino = rn.st_ino;
  stbuf->st_rdev = rn.st_rdev;
  
}

// GetDirList() returns lists of all files and folders in szDir
void GetDirList(const char* szDir, std::vector<string> &vFiles, std::vector<string> &vDirs) {
  fs::path full_path( fs::initial_path<fs::path>() );
  full_path = fs::system_complete( fs::path( szDir, fs::native ) );
	
  if (!fs::exists( full_path )){
    std::cout << "\nNot found: " << full_path.native_file_string() << std::endl;
    return;
  }
  if (fs::is_directory( full_path )){
    fs::directory_iterator end_iter;
    for ( fs::directory_iterator dir_itr( full_path );
         dir_itr != end_iter;
         ++dir_itr ) {
      try {
        if ( fs::is_directory( dir_itr->status() ) ){
          vDirs.push_back(dir_itr->path().leaf());
        }
        else if ( fs::is_regular( dir_itr->status() ) ) {
          vFiles.push_back(dir_itr->path().leaf());
        }
        //  else { // We don't handle non-regular files, Thank you.
        //    vFiles.push_back(dir_itr->path().leaf());
        // }
      }
      catch ( const std::exception & ex ) {
        std::cout << dir_itr->path().leaf() << " " << ex.what() << std::endl;
      }
    }   
  } else {
    vFiles.push_back(full_path.native_file_string());
  }
}	// GetDirList (Boost)

// Extracts the file name from a full path
void ExtractFilename(const char* szPath, string& szOutFile)
{
  fs::path full_path(szPath);
  szOutFile = full_path.leaf();
}	// ExtractFilename

void ExtractParentDirname(const char *szPath, string &szOutDir){
  fs::path full_path( szPath );
  szOutDir = full_path.branch_path().string();
  //szOutDir = (char *)malloc(full_path.leaf().length() + 1);
  //strcpy(szOutDir,parent.c_str());  
}

// Extracts the directory path (including slash) from a full path
void ExtractDirname(char* szPath, char* szOutDir)
{
	strcpy(szOutDir, szPath);
	char* szPtr = strrchr(szOutDir, '\\');
	if (szPtr == NULL)
		szPtr = strrchr(szOutDir, '/');
  
	if (szPtr != NULL)
		szPtr[1] = 0;
}	// ExtractDirname


// Doesn't include dot
void ExtractExtension(char* szPath, char* szOutExt)
{
	char* szSlashPtr = strrchr(szPath, '\\');
	if (szSlashPtr == NULL)
		szSlashPtr = strrchr(szPath, '/');
	char* szDotPtr = strrchr(szPath, '.');
  
	if ( (szDotPtr == NULL) || (szDotPtr < szSlashPtr) )
		sprintf(szOutExt, "");
	else
		strcpy(szOutExt, &(szDotPtr[1]));
}	// ExtractExtension


// Replaces all \'s with /'s
void ReplaceSlashes(char* szPath)
{
	char* szPtr = strrchr(szPath, '\\');
	while (szPtr != NULL)
	{
		szPtr[0] = '/';
		szPtr = strrchr(szPath, '\\');
	}
}	// ReplaceSlashes

// Check if these two files are identical
bool AreFilesSame(char* szFile1, char* szFile2)
{
	// Open files
	FILE* fp1 = fopen(szFile1, "rb");
	if (fp1 == NULL)
	{
		printf("ProcessFile: failed to load %s\n", szFile1);
		return false;
	}
	FILE* fp2 = fopen(szFile2, "rb");
	if (fp2 == NULL)
	{
		fclose(fp1);
		printf("ProcessFile: failed to load %s\n", szFile2);
		return false;
	}
  
	// Compute file sizes
	fseek(fp1 , 0 , SEEK_END);
	int nSize1 = ftell(fp1);
	rewind(fp1);
	fseek(fp2 , 0 , SEEK_END);
	int nSize2 = ftell(fp2);
	rewind(fp2);
	if (nSize1 != nSize2)
	{
		fclose(fp1);
		fclose(fp2);
		return false;
	}
  
	bool bSame = true;
	int* pBuf1 = (int*) malloc(ringer::g_ringer_constants.RINGER_BLOCK_SIZE+8);
	int* pBuf2 = (int*) malloc(ringer::g_ringer_constants.RINGER_BLOCK_SIZE+8);
	while (nSize1 > 0)
	{
		//read file chunks into buffers
    // TODO: Better Cast here.
		int nRead = min(nSize1, (int)ringer::g_ringer_constants.RINGER_BLOCK_SIZE);
		fread(pBuf1, 1, nRead, fp1);
		fread(pBuf2, 1, nRead, fp2);
		nSize1 -= nRead;
    
		// Check for similarities
		int nNumInts = (nRead+3)/4;
		for (int i = 0; i < nNumInts; i++)
		{
			if (pBuf1[i] != pBuf2[i])
			{
				bSame = false;
				nSize1 = 0; // break while loop
				break;
			}
		}
	}
  
	fclose(fp1);
	fclose(fp2);
	free(pBuf1);
	free(pBuf2);
	return bSame;
}	// AreFilesSame

/**
 int getIpAddr(string *)
 
 Returns the best guess as to what the system's external IP address is.  
 **/
int GetIpAddress(ringer::host_t* name){
  
  name->host_name = RINGER_DEFAULT_NAME;
  int count = 0;
  struct ifaddrs *ifa = NULL, *ifp = NULL;
  
  if (getifaddrs (&ifp) < 0)
  {
    perror ("getifaddrs");
    return -1;
  }
  
  for (ifa = ifp; ifa; ifa = ifa->ifa_next)
  {
    char ip[ RINGER_MAX_IP_SIZE ];
    socklen_t salen;
    
    if (ifa->ifa_addr->sa_family == AF_INET){
      salen = sizeof (struct sockaddr_in);
      
      if (getnameinfo (ifa->ifa_addr, salen,
                       ip, sizeof (ip), NULL, 0, NI_NUMERICHOST) < 0)
      {
        perror ("getnameinfo");
        continue;
      }
      
      if(count == RINGER_GOOD_HOSTIP_COUNT ){
        name->host_name = ip;
      }
      
      count++;
    }
  }
  
  freeifaddrs (ifp);
  
  return 0;
}

// Returns a float between 0 and 1, (almost certainly) unique to the
// extension, where all three chars are weighted about equally
float HashExtension(char* szPath)
{
	char szExt[MAX_PATH];
	ExtractExtension(szPath, szExt);
	strupr(szExt);
	int nLen = (int) strlen(szExt);
	int nTemp, nHash = 0;
	int nPow35[] = { 1, 35, 1225, 42875 }; // powers of 35
	for (int i = 0; i < min(3, nLen); i++)
	{
		// Shift ASCII code to the 0-35 range
		nTemp = max(0, szExt[i]-48);
		if (nTemp >= 17)
			nTemp -= 7; // shift letters down next to numbers
		nHash += nTemp * (nPow35[3] + nPow35[i]);
	}
	float fHash = ((float)nHash)/4501875.0f; // divide by max possible value
	fHash = (fHash-0.5f)*2.0f;
	return (fHash < 0 ? -fHash : fHash); 
}	// HashExtension

bool IsFile(char *s_path){
  fs::path full_path( fs::initial_path<fs::path>() );
  full_path = fs::system_complete( fs::path( s_path, fs::native ) );
  if (fs::is_regular( full_path )){
    return true;
  }
  return false;
}
bool IsDir(char *s_path){
  fs::path full_path( fs::initial_path<fs::path>() );
  full_path = fs::system_complete( fs::path( s_path, fs::native ) );
  if (fs::is_directory( full_path )){
    return true;
  }
  return false;
}

/**
 Parse any options given
 */
int parse_ringer_args(int argc, char **argv, ringer::RingerNetworkArgs* net_args, char** fuse_argv){
  
  std::string config_file = ringer::RINGER_DEFUALT_CONFIG_FILE_PATH;
  int fuse_argc = 1;
     
  /* 1. CREATE AN OBJECT */
  AnyOption *opt = new AnyOption();
  //opt->noPOSIX(); /* do not check for POSIX style character options */
  //opt->setVerbose(); /* print warnings about unknown options */
  //opt->autoUsagePrint(true); /* print usage for bad options */
  
  /* 3. SET THE USAGE/HELP   */
  opt->addUsage( "" );
  opt->addUsage( "Usage: " );
  opt->addUsage( "" );
  opt->addUsage( " -h  --help                               Prints this help " );
  opt->addUsage( " -d  --debug                              Run in Debug mode (implies -f) " );
  opt->addUsage( " -f                                       foreground operation" );
  opt->addUsage( " -s                                       disable multi-threaded operation" );
  opt->addUsage( "     --nonempty                           Mount on a non-empty directory" );
  opt->addUsage( " -v  --version                            Print Version info " );
  opt->addUsage( " -p  --mds_port 8072                      MDS port " );
  opt->addUsage( " -n  --mds_address 127.0.0.1              MDS host " );
  opt->addUsage( "     --fileserver_bind_address 127.0.0.1  Server host ");
  opt->addUsage( "     --fileserver_bind_port 8072          Server Port");
  opt->addUsage( "     --config config_file                 Use config file ");
  opt->addUsage( "     --trust_file cert_file.oem           OpenPGP cert. file ");
  opt->addUsage( "     --public_key key_file.pem            OpenPGP key file ");
  opt->addUsage( "     --private_key ring_file.pem          OpenPGP priv. key file ");
  opt->addUsage( "     --tagfile tag_file                   SimHash tag file ");
  opt->addUsage( "     --logger log.cfg                     Log4cxx configuration");
  opt->addUsage( "     --db_env evn                         Directory where the db files are stored");
  opt->addUsage( "     --o fuse mount option                Refer to the fuse docs.");
  opt->addUsage( "" );
  
  opt->setFlag(  "help", 'h' );  
  opt->setFlag(  "debug", 'd' );  
  opt->setFlag(  "fore", 'f' );  
  opt->setFlag(  "single", 's' );   
  opt->setFlag(  "version", 'v' );   
  opt->setOption(  "mds_port", 'p' ); 
  opt->setOption(  "mds_address", 'n' );   
  opt->setOption(  "fileserver_bind_address"  );      
  opt->setOption(  "fileserver_bind_port"  );  
  opt->setOption(  "local_cache_dir"  ); 
  opt->setOption(  "config"  );  
  opt->setOption(  "trust_file"  );  
  opt->setOption(  "public_key"  ); 
  opt->setOption(  "private_key"  );
  opt->setOption(  "tagfile"   ); 
  opt->setOption(  "logger"  ); 
  opt->setOption(  "db_env"  ); 
  opt->setOption(  "option", 'o' ); 
  
  /* go through the command line and get the options  */
  opt->processCommandArgs( argc, argv );
  
  if( ! opt->hasOptions()) { /* print usage if no options */
    opt->printUsage();
    delete opt;
		exit(-1);
	}
  
  /* 6. GET THE VALUES */
  if( opt->getFlag( "help" ) || opt->getFlag( 'h' ) ){ 
    opt->printUsage();
    exit(-1);
  }  
  if( opt->getFlag( "version" ) || opt->getFlag( 'v' ) ){ 
    std::cout << ringer::RINGER_CLIENT_VERSION_STRING << std::endl;
    exit(-1);
  } 
  if( opt->getFlag( "debug" ) || opt->getFlag( 'd' ) ){ 
    strncpy(fuse_argv[fuse_argc], "-d\0", 3 );
    fuse_argc++;
  } 
  if(opt->getValue( 'o' ) || opt->getValue("option")){ 
    strncpy(fuse_argv[fuse_argc], "-o\0", 3 );
    fuse_argc++;
    string optVal = opt->getValue( 'o' );
    optVal += "\0";
    strncpy(fuse_argv[fuse_argc], optVal.c_str(), optVal.length()+1);
    fuse_argc++;
  } 
  if(opt->getFlag( 'f' )){ 
    strncpy(fuse_argv[fuse_argc], "-f\0", 3 );
    fuse_argc++;
  } 
  if(opt->getFlag( 's' )){ 
    strncpy(fuse_argv[fuse_argc], "-s\0", 3 );
    fuse_argc++;
  } 
  if( opt->getValue( 'p' ) != NULL  || opt->getValue( "mds_port" ) != NULL  )
    net_args->mds_port = atoi(opt->getValue( "mds_port" ));
  if( opt->getValue( 'n' ) != NULL  || opt->getValue( "mds_address" ) != NULL  )
    net_args->mds_host = (string)opt->getValue( "mds_address" );  
  if( opt->getValue( "fileserver_bind_address" ) != NULL  )
    net_args->self_host = (string)opt->getValue( "fileserver_bind_address" );
  if( opt->getValue( "fileserver_bind_port" ) != NULL  )
    net_args->self_port = atoi(opt->getValue( "fileserver_bind_port" ));   
  if( opt->getValue( "config" ) != NULL  )
    config_file = (string)(opt->getValue( "config" ));   
    
  // SSL stuff  
  if( opt->getValue( "trust_file" ) != NULL  )
    net_args->trust_file = string(opt->getValue( "trust_file" ));   
  if( opt->getValue( "public_key" ) != NULL  )
    net_args->public_key = string(opt->getValue( "public_key" ));
  if( opt->getValue( "private_key" ) != NULL  )
    net_args->private_key = string(opt->getValue( "private_key" ));
  if( opt->getValue( "logger" ) != NULL  )
    net_args->logging_conifg = (string)(opt->getValue( "logger" )); 
  if( opt->getValue( "db_env" ) != NULL  )
    net_args->db_env = (string)(opt->getValue( "db_env" ));
     
  
  //Now, parse the config file
  opt->processFile( config_file.c_str() );
  
  // and get the values again  
  if( opt->getValue( 'p' ) != NULL  || opt->getValue( "mds_port" ) != NULL  )
    net_args->mds_port = atoi(opt->getValue( "mds_port" ));
  if( opt->getValue( 'n' ) != NULL  || opt->getValue( "mds_address" ) != NULL  )
    net_args->mds_host = (string)opt->getValue( "mds_address" );  
  if( opt->getValue( "fileserver_bind_address" ) != NULL  )
    net_args->self_host = (string)opt->getValue( "fileserver_bind_address" );
  if( opt->getValue( "fileserver_bind_port" ) != NULL  )
    net_args->self_port = atoi(opt->getValue( "fileserver_bind_port" )); 
  if( opt->getValue( "local_cache_dir" ) != NULL  )
    net_args->local_cache_dir = (string)(opt->getValue( "local_cache_dir" ));   
  if( opt->getValue( "logger" ) != NULL  )
    net_args->logging_conifg = (string)(opt->getValue( "logger" )); 
  if( opt->getValue( "db_env" ) != NULL  )
    net_args->db_env = (string)(opt->getValue( "db_env" ));
  
   // PGP stuff  
  if( opt->getValue( "trust_file" ) != NULL  )
    net_args->trust_file = string(opt->getValue( "trust_file" ));   
  if( opt->getValue( "public_key" ) != NULL  )
    net_args->public_key = string(opt->getValue( "public_key" ));
  if( opt->getValue( "private_key" ) != NULL  )
    net_args->private_key = string(opt->getValue( "private_key" ));  
  if( opt->getValue( "tagfile" ) != NULL  )
    net_args->simhash_tags_file = string(opt->getValue( "tagfile" ));  
    
  for( int i = 0 ; i < opt->getArgc() ; i++ ){
    fuse_argv[fuse_argc] = opt->getArgv( i );
    fuse_argc++;
	}  
  
  return fuse_argc;
}


